/*
 * IM - Image I/O Library
 * Copyright (c) 1996 CENPES/Petrobras, TeCGraf/PUC-Rio.
 * Antonio E. Scuri - scuri@tecgraf.puc-rio.br
 * 
 * IMLED.C
 * 
 * Contains the implementation of the LED file format functions.
 *
 * History:
 *          1996.04.05 - Changed the structure of the module to the new format.
 *          1996.06.14 - Finished inicial version.
 *
 */

#include "im.h"
#include "imio.h"
#include "imled.h"
#include "binfile.h"


#include <stdlib.h>
#include <memory.h>
#include <stdio.h>
#include <string.h>


/* Sample LED Image
LEDImage = IMAGE[
0= "0 0 0",
1= "192 192 192",
2= "0 0 128",
3= "255 255 255"]
(20,19
,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1
,1,1,0,3,3,3,3,0,0,1,1,1,1,1,1,1,1,1,1,1
,1,1,0,3,3,3,3,0,3,0,1,1,1,1,1,1,1,1,1,1
,1,1,0,3,0,0,3,0,2,2,2,2,2,2,1,1,1,1,1,1
,1,1,0,3,3,3,3,3,2,3,3,3,3,2,2,1,1,1,1,1
,1,1,0,3,0,0,0,0,2,3,3,3,3,2,3,2,1,1,1,1
,1,1,0,3,3,3,3,3,2,3,0,0,3,2,2,2,2,1,1,1
,1,1,0,3,0,0,0,0,2,3,3,3,3,3,3,3,2,1,1,1
,1,1,0,3,3,3,3,3,2,3,0,0,0,0,0,3,2,1,1,1
,1,1,0,0,0,0,0,0,2,3,3,3,3,3,3,3,2,1,1,1
,1,1,1,1,1,1,1,1,2,3,0,0,0,0,0,3,2,1,1,1
,1,1,1,1,1,1,1,1,2,3,3,3,3,3,3,3,2,1,1,1
,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,1,1,1
,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
)
*/

typedef struct _imFileLED
{
  binFile* file_bin;          /* the binary file handle */
  unsigned long offset;      /* image offset */
} imFileLED;


static int imLEDReadNextInteger(binFile* file_bin, int* value)
{
  int c = 0, found = 0;
  static char buffer[10];

  while (!found)
  {
    binFileRead1ByteBuf(file_bin, &((unsigned char*)buffer)[c], 1);

    /* if it's a number increments the number of characters readed */
    if (buffer[c] >= (int)'0' && buffer[c] <= (int)'9')
      c++;
    else
    {
      /* if it's not a number and we readed some characters convert them to an integer */
      if (c > 0)
      {
        buffer[c] = 0;
        sscanf(buffer, "%d", value);
        found = 1;
      }
    }

    if (binFileError(file_bin) || c > 10)
      return IM_ERR_READ;
  } 

  return IM_ERR_NONE;
}


static imFile* imLEDReadOpen(char* filename, int *err)
{
  char id[4];
  unsigned char byte_value;
  int found = 0;
  imFile *ifile = (imFile*)malloc(sizeof(imFile));
  imFileLED *file_led = (imFileLED*)malloc(sizeof(imFileLED));
  binFile* file_bin;

  ifile->file = file_led;

  /* opens the binary file for reading with intel byte order */
  file_bin = binFileOpen(filename, 0, 0);
  if (file_bin == NULL)
  {
    free(file_led);
    free(ifile);
    if (err) *err = IM_ERR_OPEN;
    return NULL;
  }

  file_led->file_bin = file_bin;

  ifile->type = IM_MAP;
  ifile->compress = IM_NONE;

  binFileRead1ByteBuf(file_bin, (unsigned char*)id, 3);
  id[3] = 0;

  if (strcmp(id, "LED") != 0)
  {
    binFileClose(file_bin);
    free(file_led);
    free(ifile);
    if (err) *err = IM_ERR_FORMAT;
    return NULL;
  }

  /* we must skip the "= IMAGE" token */
  ifile->pal_size = -1;

  /* search for the image width and height begining */
  while (!found)
  {
    binFileRead1ByteBuf(file_bin, &byte_value, 1);

    if (byte_value == '(')
      found = 1;

    if (byte_value == '=')
      ifile->pal_size++;

    if (binFileError(file_bin))
    {
      binFileClose(file_bin);
      free(file_led);
      free(ifile);
      if (err) *err = IM_ERR_READ;
      return NULL;
    }
  } 

  imLEDReadNextInteger(file_bin, &ifile->width);
  imLEDReadNextInteger(file_bin, &ifile->height);
 
  if (binFileError(file_bin))
  {
    binFileClose(file_bin);
    free(file_led);
    free(ifile);
    if (err) *err = IM_ERR_READ;
    return NULL;
  }

  file_led->offset = binFileTell(file_bin);

  /* now initialize the imFile control data */

  ifile->split = 0;     /* image is RGB packed */
  ifile->planes[0] = 0; /* sets the default plane order since split = 0 */
  ifile->planes[1] = 1;
  ifile->planes[2] = 2;
  ifile->cur_line = ifile->height - 1;  /* first line */
  ifile->pre_colors = 1;  /* color map is before the image */

  if (err) *err = IM_ERR_NONE;

  return ifile;
}


static imFile* imLEDWriteOpen(char* filename, int width, int height, int type, int compress, int pal_size, int *err)
{
  imFile *ifile = (imFile*)malloc(sizeof(imFile));
  imFileLED *file_led = (imFileLED*)malloc(sizeof(imFileLED));
  binFile* file_bin;

  ifile->file = file_led;

  if (type == IM_RGB)
  {
    free(file_led);
    free(ifile);
    if (err) *err = IM_ERR_TYPE;
    return NULL;
  }

  if (compress == IM_COMPRESSED)
  {
    free(file_led);
    free(ifile);
    if (err) *err = IM_ERR_COMP;
    return NULL;
  }

  /* opens the binary file for writing with intel byte order */
  file_bin = binFileOpen(filename, 1, 0);
  if (file_bin == NULL)
  {
    free(file_led);
    free(ifile);
    if (err) *err = IM_ERR_OPEN;
    return NULL;
  }

  file_led->file_bin = file_bin;

  ifile->width = width;
  ifile->height = height;
  ifile->type = IM_MAP;
  ifile->pal_size = pal_size;
  ifile->compress = IM_NONE;

  /* now initialize the imFile control data */

  ifile->split = 0;     /* image is RGB packed */
  ifile->planes[0] = 0; /* sets the default plane order since split = 0 */
  ifile->planes[1] = 1;
  ifile->planes[2] = 2;
  ifile->cur_line = ifile->height - 1;  /* first line */
  ifile->pre_colors = 1;  /* color map is before the image */

  binFileWrite1ByteBuf(file_bin, (unsigned char*)"LEDImage = IMAGE", 16);

  /* tests if everything was ok */
  if (binFileError(file_bin))
  {
    binFileClose(file_bin);
    free(file_led);
    free(ifile);
    if (err) *err = IM_ERR_WRITE;
    return NULL;
  }

  if (err) *err = IM_ERR_NONE;

  return ifile;
}


static void imLEDClose(imFile* ifile)
{
  imFileLED *file_led = (imFileLED*)ifile->file;
  binFile* file_bin = file_led->file_bin;

  binFileClose(file_bin);

  free(file_led);
  free(ifile);
}


static int imLEDReadColors(imFile* ifile, long *colors)
{
  int c, r, g, b, i;
  imFileLED *file_led = (imFileLED*)ifile->file;
  binFile* file_bin = file_led->file_bin;

  binFileSeekTo(file_bin, 0);

  /* convert the color map to the IM format */
  for (c = 0; c < ifile->pal_size; c++)
  {
    imLEDReadNextInteger(file_bin, &i);
    imLEDReadNextInteger(file_bin, &r);
    imLEDReadNextInteger(file_bin, &g);
    imLEDReadNextInteger(file_bin, &b);

    colors[i] = imEncodeColor((unsigned char)r, (unsigned char)g, (unsigned char)b);

    if (binFileError(file_bin))
      return IM_ERR_READ;
  }

  return IM_ERR_NONE;
}


static int imLEDWriteColors(imFile* ifile, long *colors)
{
  int c;
  unsigned char r, g, b;
  imFileLED *file_led = (imFileLED*)ifile->file;
  binFile* file_bin = file_led->file_bin;

  binFileWrite1ByteBuf(file_bin, (unsigned char*)"[\n", 2);

  /* convert the color map from the IM format */
  for (c = 0; c < ifile->pal_size; c++)
  {
    imDecodeColor(&r, &g, &b, colors[c]);
    binFilePrintf(file_bin, "%d = \"%d %d %d\"", c, (int)r, (int)g, (int)b);

    if (c != ifile->pal_size - 1)
      binFileWrite1ByteBuf(file_bin, (unsigned char*)",\n", 2);
  }

  binFileWrite1ByteBuf(file_bin, (unsigned char*)"]\n", 2);

  if (binFileError(file_bin))
    return IM_ERR_WRITE;

  return IM_ERR_NONE;
}


static int imLEDReadLine(imFile* ifile, unsigned char *map)
{
  int x = 0, value;
  imFileLED *file_led = (imFileLED*)ifile->file;
  binFile* file_bin = file_led->file_bin;

  if (ifile->cur_line == ifile->height - 1)
    binFileSeekTo(file_bin, file_led->offset);

  while (x < ifile->width)
  {
    if (imLEDReadNextInteger(file_bin, &value) != IM_ERR_NONE)
      return IM_ERR_READ;

    map[x] = (unsigned char)value;
    x++;
  } 

  /* we have just read one more line */
  ifile->cur_line--;

  return IM_ERR_NONE;
}


static int imLEDWriteLine(imFile* ifile, unsigned char *map)
{
  int x;
  imFileLED *file_led = (imFileLED*)ifile->file;
  binFile* file_bin = file_led->file_bin;

  if (ifile->cur_line == ifile->height - 1)
    binFilePrintf(file_bin, "(%d,%d\n", ifile->width, ifile->height);

  for (x = 0; x < ifile->width; x++)
    binFilePrintf(file_bin, ",%d", (int)map[x]);
  
  binFileWrite1ByteBuf(file_bin, (unsigned char*)"\n", 1);

  if (ifile->cur_line == 0)
    binFileWrite1ByteBuf(file_bin, (unsigned char*)")", 1);
  
  if (binFileError(file_bin))
    return IM_ERR_WRITE;     

  /* we have just write one more line */
  ifile->cur_line--;

  return IM_ERR_NONE;
}




static int imLEDRegisterCallback(imCallback cb, int id)
{
  (void)cb;
  (void)id;
  /* callback not found */
  return 0;
}


static imFormat _imFormatLED =
{
  0,
  imLEDRegisterCallback,
  imLEDReadOpen,
  imLEDWriteOpen,
  imLEDClose,
  imLEDReadColors,
  imLEDWriteColors,
  imLEDReadLine,
  imLEDWriteLine,
  NULL,
  NULL
};

imFormat* imFormatLED(void)
{
  return &_imFormatLED;
}
